package com.neuronbit.businessflow.intellij.dialog;

import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.ide.util.TreeChooser;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.JavaCodeFragmentFactory;
import com.intellij.psi.PsiCodeFragment;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiType;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.components.JBList;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.ui.JBInsets;
import com.intellij.util.ui.JBUI;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static com.intellij.util.indexing.DumbModeAccessType.RELIABLE_DATA_ONLY;

public class ReturnTypeListPanel extends JPanel {
    protected JPanel myNamePanel;
    protected EditorTextField myNameField;
    protected EditorTextField myReturnTypeField;
    private final String name;
    private final PsiType expectedType;
    protected final Project myProject;
    private final TreeChooser.Filter<FieldTreeListChooseDialog.FieldTreeNode> myClassFilter;
    protected PsiCodeFragment myReturnTypeCodeFragment;
    protected final @NotNull Disposable myDisposable = new Disposable() {
        @Override
        public @NonNls String toString() {
            return this.toString();
        }

        @Override
        public void dispose() {
            this.dispose();
        }
    };
    protected JBList<String> list;
    private DefaultListModel<String> listModel;
    public static DataFlavor nodeFlavor;

    static {
        try {
            nodeFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType + ";class=\"" + DefaultMutableTreeNode.class.getName() + "\"");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public ReturnTypeListPanel(String name, PsiType expectedType, Project myProject, TreeChooser.@NotNull Filter<FieldTreeListChooseDialog.FieldTreeNode> myClassFilter) {
        super(new BorderLayout());
        this.name = name;
        this.expectedType = expectedType;
        this.myProject = myProject;
        this.myClassFilter = myClassFilter;
        init();
    }

    private void init() {
        add(createNorthPanel(), BorderLayout.NORTH);
        add(createCenterPanel(), BorderLayout.CENTER);
    }

    public Project getProject() {
        return myProject;
    }

    public List<String> getValues() {
        int size = listModel.size();
        List<String> values = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            values.add(listModel.get(i));
        }
        return values;
    }

    protected JComponent createNorthPanel() {
        final JPanel panel = new JPanel(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints(0, 0, 1, 1, 0, 1,
                GridBagConstraints.WEST,
                GridBagConstraints.HORIZONTAL,
                JBInsets.emptyInsets(),
                0, 0);

        myNamePanel = new JPanel(new BorderLayout(0, 2));
        myNameField = new EditorTextField(name);
        final JLabel nameLabel = new JLabel(RefactoringBundle.message("changeSignature.name.prompt"));
        nameLabel.setLabelFor(myNameField);
        myNameField.setEnabled(false);
        myNamePanel.add(nameLabel, BorderLayout.NORTH);
        myNamePanel.add(myNameField, BorderLayout.SOUTH);

        gbc.weightx = 1;

        JPanel typePanel = new JPanel(new BorderLayout(0, 2));
        typePanel.setBorder(JBUI.Borders.emptyRight(8));
        final JLabel typeLabel = new JLabel(RefactoringBundle.message("changeSignature.return.type.prompt"));
        myReturnTypeCodeFragment = createReturnTypeCodeFragment();
        final Document document = PsiDocumentManager.getInstance(myProject).getDocument(myReturnTypeCodeFragment);
        myReturnTypeField = createReturnTypeTextField(document);
        typeLabel.setLabelFor(myReturnTypeField);
        myReturnTypeField.setEnabled(false);
        typePanel.add(typeLabel, BorderLayout.NORTH);
        typePanel.add(myReturnTypeField, BorderLayout.SOUTH);
//            if (placeReturnTypeBeforeName()) {
//                panel.add(typePanel, gbc);
//                gbc.gridx++;
//                panel.add(myNamePanel, gbc);
//            } else {
        panel.add(myNamePanel, gbc);
        gbc.gridx++;
        panel.add(typePanel, gbc);
//            }

        return panel;
    }

    protected PsiCodeFragment createReturnTypeCodeFragment() {
        final String returnTypeText = StringUtil.notNullize(expectedType.getCanonicalText());
        final JavaCodeFragmentFactory factory = JavaCodeFragmentFactory.getInstance(myProject);
        return factory.createTypeCodeFragment(returnTypeText, null, true, JavaCodeFragmentFactory.ALLOW_VOID);
    }

    protected EditorTextField createReturnTypeTextField(Document document) {
        return new EditorTextField(document, myProject, getFileType());
    }

    protected LanguageFileType getFileType() {
        return JavaFileType.INSTANCE;
    }

    @NotNull
    protected List<Pair<@NlsContexts.TabTitle String, JPanel>> createAdditionalPanels() {
        return Collections.emptyList();
    }

    protected JComponent createCenterPanel() {
        listModel = new DefaultListModel<>();
        list = new JBList<>(listModel);
        list.setCellRenderer(new ListCellRendererWithDeleteButton());

        list.setDropMode(DropMode.INSERT);
        list.setTransferHandler(new TransferHandler() {
            public boolean canImport(TransferHandler.TransferSupport support) {
                return support.isDataFlavorSupported(nodeFlavor);
            }

            public boolean importData(TransferHandler.TransferSupport support) {
                if (!canImport(support)) {
                    return false;
                }

                try {
                    Transferable t = support.getTransferable();
                    FieldTreeListChooseDialog.FieldTreeNode data = (FieldTreeListChooseDialog.FieldTreeNode) t.getTransferData(nodeFlavor);
                    addValue(data);
                    return true;
                } catch (Exception e) {
                    e.printStackTrace();
                }

                return false;
            }
        });

        JPanel dummyPanel = new JPanel();
        dummyPanel.setLayout(new BorderLayout());
//        JBScrollPane pane = new JBScrollPane(list);
        JPanel pane = ToolbarDecorator.createDecorator(list)
                .disableAddAction()
                .createPanel();
        dummyPanel.add(pane, BorderLayout.CENTER);
        dummyPanel.setBorder(JBUI.Borders.emptyTop(5));
        return dummyPanel;
    }

    /**
     * Gets psi method info.
     *
     * @param nodes the nodes
     * @return the psi method info
     */
    protected String getPsiMethodInfo(FieldTreeListChooseDialog.FieldTreeNode nodes) {
        if (null == nodes) {
            return "";
        }
        return nodes.getValue().getQualifiedName();
    }

    public void addValue(FieldTreeListChooseDialog.FieldTreeNode data) {
        ReadAction
                .nonBlocking(() -> RELIABLE_DATA_ONLY.ignoreDumbMode(() -> myClassFilter.isAccepted(data)))
                .finishOnUiThread(getModalityState(), isAccepted -> {
                    if (isAccepted) {
                        String info = getPsiMethodInfo(data);
                        if (!listModel.contains(info)) {
                            listModel.addElement(info);
                        }
                    }
                })
                .submit(AppExecutorUtil.getAppExecutorService());
    }

    private @NotNull ModalityState getModalityState() {
        return ModalityState.stateForComponent(getRootPane());
    }

    public void addValue(List<String> defaultValue) {
        for (String info : defaultValue) {
            if (!listModel.contains(info)) {
                listModel.addElement(info);
            }
        }
    }

    private class ListCellRendererWithDeleteButton extends JPanel implements ListCellRenderer<String> {
        private JLabel label;

        public ListCellRendererWithDeleteButton() {
            setLayout(new BorderLayout());
            label = new JLabel();
            add(label, BorderLayout.CENTER);
        }

        public Component getListCellRendererComponent(JList<? extends String> list, String value, int index, boolean isSelected, boolean cellHasFocus) {
            label.setText(value);
            if (isSelected) {
                setBackground(list.getSelectionBackground());
                setForeground(list.getSelectionForeground());
            } else {
                setBackground(list.getBackground());
                setForeground(list.getForeground());
            }
            return this;
        }
    }
}
